# `suppressPackageStartupMessages` is a function in R that suppresses the startup messages that are generated when loading a package. It can be useful to prevent the console from being cluttered with messages when you are loading multiple packages or when you don't want to see the package loading messages.
shhh <- suppressPackageStartupMessages # It's a library, so shhh!


shhh(library( mgcv ))
shhh(library(dplyr))
shhh(library(ggplot2))
shhh(library(lme4))
shhh(library(tidymv))
shhh(library(gamlss))
shhh(library(gsubfn))
shhh(library(lmerTest))
shhh(library(tidyverse))
shhh(library(boot))
shhh(library(rsample))
shhh(library(plotrix))
shhh(library(ggrepel))
shhh(library(mgcv))

# `theme_set(theme_bw())` is a command in R that sets the default theme for all subsequent plots to a theme called `theme_bw()`.
# `theme_bw()` is a built-in theme in the ggplot2 package that provides a classic black-and-white theme with white grid lines. By setting the default theme to `theme_bw()`, all plots that are created using ggplot2 will have this theme unless a different theme is explicitly specified.
theme_set(theme_bw())

# `options(digits=4)` is a command in R that sets the number of digits to display when printing numeric values.

# By default, R will print up to 7 digits for numeric values. However, you can use the `options()` function to change this behavior. In this case, `options(digits=4)` sets the number of digits to 4, which means that all numeric values will be displayed with at most 4 digits.
options(digits=4)
set.seed(444)

# The `pipe_message` function first outputs the status message using the message() function. This can be useful for tracking the progress of a long computation or for providing information to the user.
# After printing the message, the function returns the .data argument unchanged. This allows the function to be used as a "pipe" in a data processing pipeline, where the output of one function is used as the input to the next function.
pipe_message = function(.data, status) {message(status); .data}

Read in MoTR Data

# Interesting way of reading multiple files in one directory!

file_prefix = "../reading_measures/cleaned_f160"
fnames = list.files(path=file_prefix)

# fnames: [1] "reader_55_reading_measures.csv" "reader_56_reading_measures.csv"
# [3] "reader_59_reading_measures.csv" "reader_61_reading_measures.csv"

# Read in the data
df = data.frame()

# uses the mutate() function from the dplyr package to create a new column in a data frame called subj. The new column is created by removing the substring _reading_measures.csv from the values in an existing column called f.
for (f in fnames) {
  temp = read.csv(paste0(file_prefix, "/", f)) %>%
    mutate(subj = str_remove(f, "_reading_measures.csv")) %>%
    dplyr::select(expr_id, cond_id, para_nr, word, word_nr, first_duration, total_duration,
                  gaze_duration, go_pass_time, FPReg, subj) %>%
    rename(go_past_time = go_pass_time)
  df = rbind(df, temp)
}
df

# `gather will take column 6-9's column name make it rows, name 'metric', take their value as another row`
motr_df = df %>%
  mutate(expr_id = if_else(expr_id == 1, "Attachment", "Provo")) %>%
  gather(metric, value, 6:10)

motr_df
NA
# Average across subjects
motr_agg_df = motr_df %>%
  drop_na() %>%
  group_by(expr_id, cond_id, para_nr, word, word_nr, metric) %>%
    summarise(value = mean(value)) %>%    # sum up four subjects for each metric and divide by 4.
  ungroup() %>%
  arrange(expr_id, cond_id, para_nr, word_nr)   # like sort in python
`summarise()` has grouped output by 'expr_id', 'cond_id', 'para_nr', 'word', 'word_nr'. You can override using the `.groups` argument.
# View(motr_agg_df)

motr_agg_df

table(motr_df$subj)

reader_64 reader_66 reader_67 reader_68 reader_70 
    11990     11990     11990     11990     11990 
# reader_55 reader_56 reader_59 reader_61 
#    3100      3100      3100      3100

motr_provo_df = motr_agg_df %>%
  filter(expr_id == "Provo") %>%
  rename(text_id = para_nr,
         word_text_idx = word_nr,
         motr_value = value) %>%
  dplyr::select(-expr_id, -cond_id)

motr_provo_df
NA

Comparison to Provo

# Read in Provo surprisal, frequency and length data
provo_modeling_df = read.csv("../ancillary_data/provo_df.csv") %>%
  dplyr::select(text_id, sent_id, trigger_idx, word, freq, surp, len) %>%
  rename(word_idx = trigger_idx)

provo_modeling_df
NA
# Read in Provo eyetracking data

provo_raw_df = read.csv("../ancillary_data/provo_eyetracking.csv")

provo_eyetracking_df = provo_raw_df %>%
  dplyr::select(Participant_ID, Text_ID, Sentence_Number, Word_In_Sentence_Number, IA_ID, Word,IA_FIRST_FIXATION_DURATION, IA_FIRST_FIX_PROGRESSIVE, IA_FIRST_RUN_DWELL_TIME, IA_DWELL_TIME, IA_REGRESSION_PATH_DURATION, IA_REGRESSION_OUT) %>%
  rename( first_duration = IA_FIRST_FIXATION_DURATION,    # whether it is first pass?
          gaze_duration = IA_FIRST_RUN_DWELL_TIME,
          total_duration = IA_DWELL_TIME,
          go_past_time = IA_REGRESSION_PATH_DURATION,
          FPReg = IA_REGRESSION_OUT,
          subj = Participant_ID,
          text_id = Text_ID,
          sent_id = Sentence_Number,
          word_idx = Word_In_Sentence_Number,
          word_text_idx = IA_ID,
          word = Word,
          ff_progressive = IA_FIRST_FIX_PROGRESSIVE) %>% # notice:average across subj, binary(0,1) becomes float.
  mutate(gaze_duration = ifelse(ff_progressive == 0, 0, gaze_duration),
         go_past_time = ifelse(ff_progressive == 0, 0, go_past_time)) %>%
  dplyr::select(-ff_progressive) %>%
  gather(metric, value, 7:11) %>%      # not include FFReg which is in column 11
  mutate(value = if_else(is.na(value), as.integer(0), as.integer(value))) %>%
  drop_na() %>%   # actually, drop first word in a sentence
  group_by(text_id, word_text_idx, sent_id, word_idx, word, metric) %>%
  summarise(value = mean(value)) %>%
  ungroup()
`summarise()` has grouped output by 'text_id', 'word_text_idx', 'sent_id', 'word_idx', 'word'. You can override using the `.groups` argument.
# View(provo_eyetracking_df)
provo_eyetracking_df
NA
provo_df = merge(provo_eyetracking_df, provo_modeling_df, by=c("text_id", "sent_id", "word_idx")) %>%
  mutate(word_text_idx = as.integer(word_text_idx - 1)) %>%
  arrange(text_id, sent_id, word_idx)
provo_df

provo_df = merge(provo_df, motr_provo_df, by=c("text_id", "word_text_idx", "metric")) %>%
  rename(eyetr_value = value) %>%
  arrange(text_id, sent_id, word_idx) %>%
  filter(word.x == word) %>%      #word.y has no captical word
  dplyr::select(-word.x, -word.y) %>%
  mutate(motr_outlier = if_else(motr_value > (mean(motr_value) + 3 * sd(motr_value) ), T, F)) %>%
  filter(motr_outlier == F) %>%     # clear outlier -> 13 was filtered.
  gather(measure, value, c("eyetr_value", "motr_value"))  %>%
  # filter(metric != "first_duration") %>%
  filter(metric != "FPReg")

# View(provo_df)
provo_df %>%
  ggplot(aes(x = value, color=metric)) +
    geom_density() +
    facet_wrap(.~measure) +
    xlab("Reading Measure")
Error in `combine_vars()`:
! Faceting variables must have at least one value
Backtrace:
 1. base (local) `<fn>`(x)
 2. ggplot2:::print.ggplot(x)
 4. ggplot2:::ggplot_build.ggplot(x)
 5. layout$setup(data, plot$data, plot$plot_env)
 6. ggplot2 (local) setup(..., self = self)
 7. self$facet$compute_layout(data, self$facet_params)
 8. ggplot2 (local) compute_layout(..., self = self)
 9. ggplot2::combine_vars(data, params$plot_env, vars, drop = params$drop)

provo_df %>%
  filter(measure == "motr_value") %>%
  ggplot(aes(x = value, color=metric)) +
    geom_density() +
    xlab("Reading Measure") +
    ggtitle("Density plot of value for motr")

provo_df %>%
  filter(measure == "eyetr_value") %>%
  ggplot(aes(x = value, color=metric)) +
    geom_density() +
    xlab("Reading Measure") +
    ggtitle("Density plot of value for eyetr")

gd_df = provo_df %>% filter(metric == "total_duration") %>% spread(measure, value)
# View(gd_df)

cor.test(gd_df$eyetr_value, gd_df$motr_value)          #cor rise from 0.529 to 0.5989 

    Pearson's product-moment correlation

data:  gd_df$eyetr_value and gd_df$motr_value
t = 18, df = 377, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.6113 0.7226
sample estimates:
   cor 
0.6707 
provo_df %>%
  spread(measure, value) %>%
  ggplot(aes(x = motr_value, y=eyetr_value, color=metric)) +
    geom_point(alpha = 0.2) +
    facet_wrap(.~metric, scales="free") +
    geom_smooth()


# ggsave("../visualization/metric_cor.png", device = "png", width = 6, height = 2.5)
gd_df_2 = provo_df %>% filter(metric == "first_duration") %>% spread(measure, value)

cor.test(gd_df_2$eyetr_value, gd_df_2$motr_value)

    Pearson's product-moment correlation

data:  gd_df_2$eyetr_value and gd_df_2$motr_value
t = 18, df = 379, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.6220 0.7305
sample estimates:
 cor 
0.68 
gd_df_3 = provo_df %>% filter(metric == "gaze_duration") %>% spread(measure, value)

cor.test(gd_df_3$eyetr_value, gd_df_3$motr_value)

    Pearson's product-moment correlation

data:  gd_df_3$eyetr_value and gd_df_3$motr_value
t = 19, df = 379, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.6406 0.7447
sample estimates:
   cor 
0.6963 
gd_df_4 = provo_df %>% filter(metric == "go_past_time") %>% spread(measure, value)

cor.test(gd_df_4$eyetr_value, gd_df_4$motr_value)

    Pearson's product-moment correlation

data:  gd_df_4$eyetr_value and gd_df_4$motr_value
t = 14, df = 368, p-value <2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.5197 0.6530
sample estimates:
   cor 
0.5903 
provo_df %>%
  gather(word_prop, word_prop_val, c("freq", "len", "surp")) %>%
  filter(metric == "gaze_duration") %>%
  ggplot(aes(x = value, y=word_prop_val, color = measure)) +
    geom_point(alpha = 0.2) +
    facet_grid(word_prop~measure, scales="free") +
    geom_smooth() +
    xlab("Reading Measure")


# ggsave("../visualization/word_prop_comps.png", device = "png", width = 6, height = 3)
provo_df %>%
  ggplot(aes(x = value, y=freq, color=metric)) +
    geom_point(alpha = 0.2) +
    facet_grid(metric~measure, scales="free") +
    geom_smooth()

provo_df %>%
  ggplot(aes(x = value, y=surp, color=metric)) +
    geom_point(alpha = 0.2) +
    facet_grid(metric~measure, scales="free") +
    geom_smooth()

Shape of surprisal / RT relationship


fit_gam_inner = function(bootstrap_sample, mean_predictors) {
  
  df = bootstrap_sample$data
  weights = tabulate(as.integer(bootstrap_sample), nrow(df))
  
  m = gam(psychometric ~ s(surp, bs = 'cr', k = 6) + s(prev_surp, bs = 'cr', k = 6) + te(freq, len, bs = 'cr') + te(prev_freq, prev_len, bs = 'cr'), data = df, weights = weights)
  terms_to_predict = c("s(surp)", "s(prev_surp)")
  
  newdata = data.frame(surp=seq(0,20,by=0.1), prev_surp=mean_predictors$surp,
                       freq=mean_predictors$freq, prev_freq=mean_predictors$freq,
                       len=mean_predictors$freq, prev_len=mean_predictors$freq)

  # Returns a matrix N_samples * N_terms.
  per_term_predictions = predict(m, newdata=newdata, terms=terms_to_predict, type="terms")

  # Additive model -- sum across predictor response contributions (matrix columns).
  predictions = rowSums(per_term_predictions)

  return(newdata %>% mutate(y=predictions))
}

fit_gam = function(df, mean_predictors, alpha=0.05) {
  # Bootstrap-resample data
  boot_models = df %>% bootstraps(times=10) %>% 
   # Fit a GAM and get predictions for each sample
    mutate(smoothed=map(splits, fit_gam_inner, mean_predictors=mean_predictors))
  
  # Extract mean and 5% and 95% percentile y-values for each surprisal value
  result = boot_models %>% 
    unnest(smoothed) %>% 
    dplyr::select(surp, y) %>% 
    group_by(surp) %>% 
      summarise(y_lower=quantile(y, alpha / 2), 
                y_upper=quantile(y, 1 - alpha / 2),
                y=mean(y)) %>% 
    ungroup()
  
  return (result)
}

gam_modeling_df = provo_df %>%
  spread(measure, value) %>%
  mutate(len = nchar(word)) %>%
  group_by(metric, text_id) %>%
    arrange(word_text_idx) %>%
    mutate(prev_surp = lag(surp),
           prev_freq = lag(freq),
           prev_len = lag(len),
           prev_eyetr_value = lag(eyetr_value)) %>%
  ungroup() %>%
  drop_na() %>%
  rename(psychometric = motr_value)


smooths_df = data.frame()

metrics = c("gaze_duration", "total_duration", "go_past_time", "first_duration")
for (m in metrics) {
  print(paste0("Fitting model for ", m))
  dummy_df = gam_modeling_df %>% filter(metric == m)
  mean_predictors = dummy_df %>% summarise(surp = mean(surp), len = mean(len), freq = mean(freq))
  smooths = dummy_df %>% fit_gam(., mean_predictors)
  #Fix 0 surprisal = 0 ms
  gam_smooths = smooths %>% mutate(delta = 0 - y[1], y=y + delta, y_lower= y_lower + delta, y_upper=y_upper + delta)
  smooths_df = rbind(smooths_df, gam_smooths %>% mutate(psychometric = m))
}
[1] "Fitting model for gaze_duration"
[1] "Fitting model for total_duration"
[1] "Fitting model for go_past_time"
[1] "Fitting model for first_duration"

Targeted Evaluation Data

# View(motr_df)

motr_attach_df = motr_df %>%
  filter(expr_id == "Attachment") %>%
  rename( item_id = para_nr) %>%
  mutate(item_id = as.integer(item_id)) %>%
  mutate(cond_id = as.factor(cond_id)) %>%
  mutate(cond_id = if_else(cond_id == 1, "No Comma", 
                          if_else(cond_id == 2, "Comma", 
                                  if_else(cond_id == 3, "adv_high",
                                          if_else(cond_id == 4, "adv_low",
                                                  if_else(cond_id == 5, "relative_high",
                                                          if_else(cond_id == 6, "relative_low",
                                                                  if_else(cond_id == 7, "practice", "filler")))))))) %>%
  filter(metric != "FPReg") %>%
  filter(cond_id == "relative_high" | cond_id == "relative_low") %>%

  # filter(! (item_id == 4 & cond_id == "No Comma") ) %>% # just because of alignment issues for now
  
  mutate(crit = if_else(word == "who", word_nr, as.integer(0) )) %>%
  group_by(cond_id, item_id) %>%
    mutate(crit = unique(crit)[2]) %>%
  ungroup() %>%
  mutate(word_nr = word_nr - crit)
View(motr_attach_df)


agg_motr_attach_df = motr_attach_df %>%
  drop_na() %>%
  filter(word_nr >= -2, word_nr < 6) %>%
  group_by(cond_id, word_nr, metric) %>%
    summarise( m = mean(value),
               sd = std.error(value),
               upper = m + 1.96 * sd,
               lower = m - 1.98 * sd,
               n = n()) %>%
  ungroup()
`summarise()` has grouped output by 'cond_id', 'word_nr'. You can override using the `.groups` argument.
View(agg_motr_attach_df)

agg_motr_attach_df %>%
  ggplot(aes(x = word_nr, y = m, color = cond_id)) +
    geom_rect(aes(xmin = 2.5, xmax = 5.5, ymin = 100, ymax = 800), fill=alpha("white", 0), color = "#45ef70", linetype = "dotted") +
    geom_point() +
    geom_errorbar(aes(ymax = upper, ymin = lower), width = 0.3) +
    geom_line() +
    #geom_text(aes(label = word, y = if_else(cond_id == "Comma", 3000, 3500)), size = 2) +
    #facet_grid(para_nr~cond_id) +
  ylab("Reading Time") +
  xlab("Condition") +
  # scale_x_continuous(breaks=-2:5, labels=c("the", "man", "and", "his", "wife", "ran", "away", "from")) +
  scale_x_continuous(breaks=-2:5, labels=c('the', 'queen', 'who', 'praised', 'herself', 'all', 'the', 'time')) +
  facet_grid(~metric) +
  theme(
    legend.position = "bottom",
    axis.text.x = element_text(angle = 45, hjust = 1)
  )


#ggsave("../visualization/attachment.png", device = "png", width = 6, height = 3)

options(JULIA_HOME = "/Applications/Julia-1.8.app/Contents/Resources/julia/bin/")
# library(jglmm)
# jglmm_setup()

attach_lm_df = motr_attach_df %>%
  filter(metric == "gaze_duration") %>%
  filter(word_nr == 3) %>%
  mutate(item_id = as.factor(item_id),
         subj = as.factor(subj))

m = attach_lm_df %>%
  lmer(value ~ cond_id + (cond_id | item_id) + (cond_id | subj), data=.)
boundary (singular) fit: see help('isSingular')
summary(m)
Linear mixed model fit by REML. t-tests use Satterthwaite's method ['lmerModLmerTest']
Formula: value ~ cond_id + (cond_id | item_id) + (cond_id | subj)
   Data: .

REML criterion at convergence: 1112

Scaled residuals: 
   Min     1Q Median     3Q    Max 
-1.797 -0.639 -0.280  0.523  3.497 

Random effects:
 Groups   Name                Variance Std.Dev. Corr 
 item_id  (Intercept)         25837    160.7         
          cond_idrelative_low  2360     48.6    -1.00
 subj     (Intercept)           948     30.8         
          cond_idrelative_low 14451    120.2    1.00 
 Residual                     63112    251.2         
Number of obs: 80, groups:  item_id, 24; subj, 5

Fixed effects:
                    Estimate Std. Error    df t value Pr(>|t|)    
(Intercept)            252.1       53.8  15.5    4.68  0.00027 ***
cond_idrelative_low    -18.4       79.1   5.1   -0.23  0.82560    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Correlation of Fixed Effects:
            (Intr)
cnd_drltv_l -0.290
optimizer (nloptwrap) convergence code: 0 (OK)
boundary (singular) fit: see help('isSingular')
LS0tCnRpdGxlOiAiRXhwbG9yYXRvcnkgQW5hbHlzaXMgZm9yIE1vVFIgUmVhZGluZyBEYXRhIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KIyBgc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzYCBpcyBhIGZ1bmN0aW9uIGluIFIgdGhhdCBzdXBwcmVzc2VzIHRoZSBzdGFydHVwIG1lc3NhZ2VzIHRoYXQgYXJlIGdlbmVyYXRlZCB3aGVuIGxvYWRpbmcgYSBwYWNrYWdlLiBJdCBjYW4gYmUgdXNlZnVsIHRvIHByZXZlbnQgdGhlIGNvbnNvbGUgZnJvbSBiZWluZyBjbHV0dGVyZWQgd2l0aCBtZXNzYWdlcyB3aGVuIHlvdSBhcmUgbG9hZGluZyBtdWx0aXBsZSBwYWNrYWdlcyBvciB3aGVuIHlvdSBkb24ndCB3YW50IHRvIHNlZSB0aGUgcGFja2FnZSBsb2FkaW5nIG1lc3NhZ2VzLgpzaGhoIDwtIHN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyAjIEl0J3MgYSBsaWJyYXJ5LCBzbyBzaGhoIQoKCnNoaGgobGlicmFyeSggbWdjdiApKQpzaGhoKGxpYnJhcnkoZHBseXIpKQpzaGhoKGxpYnJhcnkoZ2dwbG90MikpCnNoaGgobGlicmFyeShsbWU0KSkKc2hoaChsaWJyYXJ5KHRpZHltdikpCnNoaGgobGlicmFyeShnYW1sc3MpKQpzaGhoKGxpYnJhcnkoZ3N1YmZuKSkKc2hoaChsaWJyYXJ5KGxtZXJUZXN0KSkKc2hoaChsaWJyYXJ5KHRpZHl2ZXJzZSkpCnNoaGgobGlicmFyeShib290KSkKc2hoaChsaWJyYXJ5KHJzYW1wbGUpKQpzaGhoKGxpYnJhcnkocGxvdHJpeCkpCnNoaGgobGlicmFyeShnZ3JlcGVsKSkKc2hoaChsaWJyYXJ5KG1nY3YpKQoKIyBgdGhlbWVfc2V0KHRoZW1lX2J3KCkpYCBpcyBhIGNvbW1hbmQgaW4gUiB0aGF0IHNldHMgdGhlIGRlZmF1bHQgdGhlbWUgZm9yIGFsbCBzdWJzZXF1ZW50IHBsb3RzIHRvIGEgdGhlbWUgY2FsbGVkIGB0aGVtZV9idygpYC4KIyBgdGhlbWVfYncoKWAgaXMgYSBidWlsdC1pbiB0aGVtZSBpbiB0aGUgZ2dwbG90MiBwYWNrYWdlIHRoYXQgcHJvdmlkZXMgYSBjbGFzc2ljIGJsYWNrLWFuZC13aGl0ZSB0aGVtZSB3aXRoIHdoaXRlIGdyaWQgbGluZXMuIEJ5IHNldHRpbmcgdGhlIGRlZmF1bHQgdGhlbWUgdG8gYHRoZW1lX2J3KClgLCBhbGwgcGxvdHMgdGhhdCBhcmUgY3JlYXRlZCB1c2luZyBnZ3Bsb3QyIHdpbGwgaGF2ZSB0aGlzIHRoZW1lIHVubGVzcyBhIGRpZmZlcmVudCB0aGVtZSBpcyBleHBsaWNpdGx5IHNwZWNpZmllZC4KdGhlbWVfc2V0KHRoZW1lX2J3KCkpCgojIGBvcHRpb25zKGRpZ2l0cz00KWAgaXMgYSBjb21tYW5kIGluIFIgdGhhdCBzZXRzIHRoZSBudW1iZXIgb2YgZGlnaXRzIHRvIGRpc3BsYXkgd2hlbiBwcmludGluZyBudW1lcmljIHZhbHVlcy4KCiMgQnkgZGVmYXVsdCwgUiB3aWxsIHByaW50IHVwIHRvIDcgZGlnaXRzIGZvciBudW1lcmljIHZhbHVlcy4gSG93ZXZlciwgeW91IGNhbiB1c2UgdGhlIGBvcHRpb25zKClgIGZ1bmN0aW9uIHRvIGNoYW5nZSB0aGlzIGJlaGF2aW9yLiBJbiB0aGlzIGNhc2UsIGBvcHRpb25zKGRpZ2l0cz00KWAgc2V0cyB0aGUgbnVtYmVyIG9mIGRpZ2l0cyB0byA0LCB3aGljaCBtZWFucyB0aGF0IGFsbCBudW1lcmljIHZhbHVlcyB3aWxsIGJlIGRpc3BsYXllZCB3aXRoIGF0IG1vc3QgNCBkaWdpdHMuCm9wdGlvbnMoZGlnaXRzPTQpCnNldC5zZWVkKDQ0NCkKCiMgVGhlIGBwaXBlX21lc3NhZ2VgIGZ1bmN0aW9uIGZpcnN0IG91dHB1dHMgdGhlIHN0YXR1cyBtZXNzYWdlIHVzaW5nIHRoZSBtZXNzYWdlKCkgZnVuY3Rpb24uIFRoaXMgY2FuIGJlIHVzZWZ1bCBmb3IgdHJhY2tpbmcgdGhlIHByb2dyZXNzIG9mIGEgbG9uZyBjb21wdXRhdGlvbiBvciBmb3IgcHJvdmlkaW5nIGluZm9ybWF0aW9uIHRvIHRoZSB1c2VyLgojIEFmdGVyIHByaW50aW5nIHRoZSBtZXNzYWdlLCB0aGUgZnVuY3Rpb24gcmV0dXJucyB0aGUgLmRhdGEgYXJndW1lbnQgdW5jaGFuZ2VkLiBUaGlzIGFsbG93cyB0aGUgZnVuY3Rpb24gdG8gYmUgdXNlZCBhcyBhICJwaXBlIiBpbiBhIGRhdGEgcHJvY2Vzc2luZyBwaXBlbGluZSwgd2hlcmUgdGhlIG91dHB1dCBvZiBvbmUgZnVuY3Rpb24gaXMgdXNlZCBhcyB0aGUgaW5wdXQgdG8gdGhlIG5leHQgZnVuY3Rpb24uCnBpcGVfbWVzc2FnZSA9IGZ1bmN0aW9uKC5kYXRhLCBzdGF0dXMpIHttZXNzYWdlKHN0YXR1cyk7IC5kYXRhfQoKYGBgCgpgYGB7cn0KCmBgYAoKIyBSZWFkIGluIE1vVFIgRGF0YQoKYGBge3J9CiMgSW50ZXJlc3Rpbmcgd2F5IG9mIHJlYWRpbmcgbXVsdGlwbGUgZmlsZXMgaW4gb25lIGRpcmVjdG9yeSEKCmZpbGVfcHJlZml4ID0gIi4uL3JlYWRpbmdfbWVhc3VyZXMvY2xlYW5lZF9mMTYwIgpmbmFtZXMgPSBsaXN0LmZpbGVzKHBhdGg9ZmlsZV9wcmVmaXgpCgojIGZuYW1lczogWzFdICJyZWFkZXJfNTVfcmVhZGluZ19tZWFzdXJlcy5jc3YiICJyZWFkZXJfNTZfcmVhZGluZ19tZWFzdXJlcy5jc3YiCiMgWzNdICJyZWFkZXJfNTlfcmVhZGluZ19tZWFzdXJlcy5jc3YiICJyZWFkZXJfNjFfcmVhZGluZ19tZWFzdXJlcy5jc3YiCgojIFJlYWQgaW4gdGhlIGRhdGEKZGYgPSBkYXRhLmZyYW1lKCkKCiMgdXNlcyB0aGUgbXV0YXRlKCkgZnVuY3Rpb24gZnJvbSB0aGUgZHBseXIgcGFja2FnZSB0byBjcmVhdGUgYSBuZXcgY29sdW1uIGluIGEgZGF0YSBmcmFtZSBjYWxsZWQgc3Viai4gVGhlIG5ldyBjb2x1bW4gaXMgY3JlYXRlZCBieSByZW1vdmluZyB0aGUgc3Vic3RyaW5nIF9yZWFkaW5nX21lYXN1cmVzLmNzdiBmcm9tIHRoZSB2YWx1ZXMgaW4gYW4gZXhpc3RpbmcgY29sdW1uIGNhbGxlZCBmLgpmb3IgKGYgaW4gZm5hbWVzKSB7CiAgdGVtcCA9IHJlYWQuY3N2KHBhc3RlMChmaWxlX3ByZWZpeCwgIi8iLCBmKSkgJT4lCiAgICBtdXRhdGUoc3ViaiA9IHN0cl9yZW1vdmUoZiwgIl9yZWFkaW5nX21lYXN1cmVzLmNzdiIpKSAlPiUKICAgIGRwbHlyOjpzZWxlY3QoZXhwcl9pZCwgY29uZF9pZCwgcGFyYV9uciwgd29yZCwgd29yZF9uciwgZmlyc3RfZHVyYXRpb24sIHRvdGFsX2R1cmF0aW9uLAogICAgICAgICAgICAgICAgICBnYXplX2R1cmF0aW9uLCBnb19wYXNzX3RpbWUsIEZQUmVnLCBzdWJqKSAlPiUKICAgIHJlbmFtZShnb19wYXN0X3RpbWUgPSBnb19wYXNzX3RpbWUpCiAgZGYgPSByYmluZChkZiwgdGVtcCkKfQpkZgoKIyBgZ2F0aGVyIHdpbGwgdGFrZSBjb2x1bW4gNi05J3MgY29sdW1uIG5hbWUgbWFrZSBpdCByb3dzLCBuYW1lICdtZXRyaWMnLCB0YWtlIHRoZWlyIHZhbHVlIGFzIGFub3RoZXIgcm93YAptb3RyX2RmID0gZGYgJT4lCiAgbXV0YXRlKGV4cHJfaWQgPSBpZl9lbHNlKGV4cHJfaWQgPT0gMSwgIkF0dGFjaG1lbnQiLCAiUHJvdm8iKSkgJT4lCiAgZ2F0aGVyKG1ldHJpYywgdmFsdWUsIDY6MTApCgptb3RyX2RmCgpgYGAKCmBgYHtyfQojIEF2ZXJhZ2UgYWNyb3NzIHN1YmplY3RzCm1vdHJfYWdnX2RmID0gbW90cl9kZiAlPiUKICBkcm9wX25hKCkgJT4lCiAgZ3JvdXBfYnkoZXhwcl9pZCwgY29uZF9pZCwgcGFyYV9uciwgd29yZCwgd29yZF9uciwgbWV0cmljKSAlPiUKICAgIHN1bW1hcmlzZSh2YWx1ZSA9IG1lYW4odmFsdWUpKSAlPiUgICAgIyBzdW0gdXAgZm91ciBzdWJqZWN0cyBmb3IgZWFjaCBtZXRyaWMgYW5kIGRpdmlkZSBieSA0LgogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKGV4cHJfaWQsIGNvbmRfaWQsIHBhcmFfbnIsIHdvcmRfbnIpICAgIyBsaWtlIHNvcnQgaW4gcHl0aG9uCgojIFZpZXcobW90cl9hZ2dfZGYpCgptb3RyX2FnZ19kZgoKdGFibGUobW90cl9kZiRzdWJqKQojIHJlYWRlcl81NSByZWFkZXJfNTYgcmVhZGVyXzU5IHJlYWRlcl82MSAKIyAgICAzMTAwICAgICAgMzEwMCAgICAgIDMxMDAgICAgICAzMTAwCgpgYGAKCmBgYHtyfQoKbW90cl9wcm92b19kZiA9IG1vdHJfYWdnX2RmICU+JQogIGZpbHRlcihleHByX2lkID09ICJQcm92byIpICU+JQogIHJlbmFtZSh0ZXh0X2lkID0gcGFyYV9uciwKICAgICAgICAgd29yZF90ZXh0X2lkeCA9IHdvcmRfbnIsCiAgICAgICAgIG1vdHJfdmFsdWUgPSB2YWx1ZSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtZXhwcl9pZCwgLWNvbmRfaWQpCgptb3RyX3Byb3ZvX2RmCgpgYGAKCgoKIyBDb21wYXJpc29uIHRvIFByb3ZvCgpgYGB7cn0KIyBSZWFkIGluIFByb3ZvIHN1cnByaXNhbCwgZnJlcXVlbmN5IGFuZCBsZW5ndGggZGF0YQpwcm92b19tb2RlbGluZ19kZiA9IHJlYWQuY3N2KCIuLi9hbmNpbGxhcnlfZGF0YS9wcm92b19kZi5jc3YiKSAlPiUKICBkcGx5cjo6c2VsZWN0KHRleHRfaWQsIHNlbnRfaWQsIHRyaWdnZXJfaWR4LCB3b3JkLCBmcmVxLCBzdXJwLCBsZW4pICU+JQogIHJlbmFtZSh3b3JkX2lkeCA9IHRyaWdnZXJfaWR4KQoKcHJvdm9fbW9kZWxpbmdfZGYKCmBgYAoKYGBge3J9CiMgUmVhZCBpbiBQcm92byBleWV0cmFja2luZyBkYXRhCgpwcm92b19yYXdfZGYgPSByZWFkLmNzdigiLi4vYW5jaWxsYXJ5X2RhdGEvcHJvdm9fZXlldHJhY2tpbmcuY3N2IikKCnByb3ZvX2V5ZXRyYWNraW5nX2RmID0gcHJvdm9fcmF3X2RmICU+JQogIGRwbHlyOjpzZWxlY3QoUGFydGljaXBhbnRfSUQsIFRleHRfSUQsIFNlbnRlbmNlX051bWJlciwgV29yZF9Jbl9TZW50ZW5jZV9OdW1iZXIsIElBX0lELCBXb3JkLElBX0ZJUlNUX0ZJWEFUSU9OX0RVUkFUSU9OLCBJQV9GSVJTVF9GSVhfUFJPR1JFU1NJVkUsIElBX0ZJUlNUX1JVTl9EV0VMTF9USU1FLCBJQV9EV0VMTF9USU1FLCBJQV9SRUdSRVNTSU9OX1BBVEhfRFVSQVRJT04sIElBX1JFR1JFU1NJT05fT1VUKSAlPiUKICByZW5hbWUoIGZpcnN0X2R1cmF0aW9uID0gSUFfRklSU1RfRklYQVRJT05fRFVSQVRJT04sICAgICMgd2hldGhlciBpdCBpcyBmaXJzdCBwYXNzPwogICAgICAgICAgZ2F6ZV9kdXJhdGlvbiA9IElBX0ZJUlNUX1JVTl9EV0VMTF9USU1FLAogICAgICAgICAgdG90YWxfZHVyYXRpb24gPSBJQV9EV0VMTF9USU1FLAogICAgICAgICAgZ29fcGFzdF90aW1lID0gSUFfUkVHUkVTU0lPTl9QQVRIX0RVUkFUSU9OLAogICAgICAgICAgRlBSZWcgPSBJQV9SRUdSRVNTSU9OX09VVCwKICAgICAgICAgIHN1YmogPSBQYXJ0aWNpcGFudF9JRCwKICAgICAgICAgIHRleHRfaWQgPSBUZXh0X0lELAogICAgICAgICAgc2VudF9pZCA9IFNlbnRlbmNlX051bWJlciwKICAgICAgICAgIHdvcmRfaWR4ID0gV29yZF9Jbl9TZW50ZW5jZV9OdW1iZXIsCiAgICAgICAgICB3b3JkX3RleHRfaWR4ID0gSUFfSUQsCiAgICAgICAgICB3b3JkID0gV29yZCwKICAgICAgICAgIGZmX3Byb2dyZXNzaXZlID0gSUFfRklSU1RfRklYX1BST0dSRVNTSVZFKSAlPiUgIyBub3RpY2U6YXZlcmFnZSBhY3Jvc3Mgc3ViaiwgYmluYXJ5KDAsMSkgYmVjb21lcyBmbG9hdC4KICBtdXRhdGUoZ2F6ZV9kdXJhdGlvbiA9IGlmZWxzZShmZl9wcm9ncmVzc2l2ZSA9PSAwLCAwLCBnYXplX2R1cmF0aW9uKSwKICAgICAgICAgZ29fcGFzdF90aW1lID0gaWZlbHNlKGZmX3Byb2dyZXNzaXZlID09IDAsIDAsIGdvX3Bhc3RfdGltZSkpICU+JQogIGRwbHlyOjpzZWxlY3QoLWZmX3Byb2dyZXNzaXZlKSAlPiUKICBnYXRoZXIobWV0cmljLCB2YWx1ZSwgNzoxMSkgJT4lICAgICAgIyBub3QgaW5jbHVkZSBGRlJlZyB3aGljaCBpcyBpbiBjb2x1bW4gMTEKICBtdXRhdGUodmFsdWUgPSBpZl9lbHNlKGlzLm5hKHZhbHVlKSwgYXMuaW50ZWdlcigwKSwgYXMuaW50ZWdlcih2YWx1ZSkpKSAlPiUKICBkcm9wX25hKCkgJT4lICAgIyBhY3R1YWxseSwgZHJvcCBmaXJzdCB3b3JkIGluIGEgc2VudGVuY2UKICBncm91cF9ieSh0ZXh0X2lkLCB3b3JkX3RleHRfaWR4LCBzZW50X2lkLCB3b3JkX2lkeCwgd29yZCwgbWV0cmljKSAlPiUKICBzdW1tYXJpc2UodmFsdWUgPSBtZWFuKHZhbHVlKSkgJT4lCiAgdW5ncm91cCgpCgojIFZpZXcocHJvdm9fZXlldHJhY2tpbmdfZGYpCnByb3ZvX2V5ZXRyYWNraW5nX2RmCgpgYGAKCgpgYGB7cn0KcHJvdm9fZGYgPSBtZXJnZShwcm92b19leWV0cmFja2luZ19kZiwgcHJvdm9fbW9kZWxpbmdfZGYsIGJ5PWMoInRleHRfaWQiLCAic2VudF9pZCIsICJ3b3JkX2lkeCIpKSAlPiUKICBtdXRhdGUod29yZF90ZXh0X2lkeCA9IGFzLmludGVnZXIod29yZF90ZXh0X2lkeCAtIDEpKSAlPiUKICBhcnJhbmdlKHRleHRfaWQsIHNlbnRfaWQsIHdvcmRfaWR4KQpwcm92b19kZgoKcHJvdm9fZGYgPSBtZXJnZShwcm92b19kZiwgbW90cl9wcm92b19kZiwgYnk9YygidGV4dF9pZCIsICJ3b3JkX3RleHRfaWR4IiwgIm1ldHJpYyIpKSAlPiUKICByZW5hbWUoZXlldHJfdmFsdWUgPSB2YWx1ZSkgJT4lCiAgYXJyYW5nZSh0ZXh0X2lkLCBzZW50X2lkLCB3b3JkX2lkeCkgJT4lCiAgZmlsdGVyKHdvcmQueCA9PSB3b3JkKSAlPiUgICAgICAjd29yZC55IGhhcyBubyBjYXB0aWNhbCB3b3JkCiAgZHBseXI6OnNlbGVjdCgtd29yZC54LCAtd29yZC55KSAlPiUKICBtdXRhdGUobW90cl9vdXRsaWVyID0gaWZfZWxzZShtb3RyX3ZhbHVlID4gKG1lYW4obW90cl92YWx1ZSkgKyAzICogc2QobW90cl92YWx1ZSkgKSwgVCwgRikpICU+JQogIGZpbHRlcihtb3RyX291dGxpZXIgPT0gRikgJT4lICAgICAjIGNsZWFyIG91dGxpZXIgLT4gMTMgd2FzIGZpbHRlcmVkLgogIGdhdGhlcihtZWFzdXJlLCB2YWx1ZSwgYygiZXlldHJfdmFsdWUiLCAibW90cl92YWx1ZSIpKSAgJT4lCiAgIyBmaWx0ZXIobWV0cmljICE9ICJmaXJzdF9kdXJhdGlvbiIpICU+JQogIGZpbHRlcihtZXRyaWMgIT0gIkZQUmVnIikKCiMgVmlldyhwcm92b19kZikKCgpgYGAKCgpgYGB7cn0KcHJvdm9fZGYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsIGNvbG9yPW1ldHJpYykpICsKICAgIGdlb21fZGVuc2l0eSgpICsKICAgIGZhY2V0X3dyYXAoLn5tZWFzdXJlKSArCiAgICB4bGFiKCJSZWFkaW5nIE1lYXN1cmUiKQojIGdnc2F2ZSgiLi4vdmlzdWFsaXphdGlvbi9kZW5zaXR5LnBuZyIsIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDIuNSkKYGBgCgpgYGB7cn0KcHJvdm9fZGYgJT4lCiAgZmlsdGVyKG1lYXN1cmUgPT0gIm1vdHJfdmFsdWUiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgY29sb3I9bWV0cmljKSkgKwogICAgZ2VvbV9kZW5zaXR5KCkgKwogICAgeGxhYigiUmVhZGluZyBNZWFzdXJlIikgKwogICAgZ2d0aXRsZSgiRGVuc2l0eSBwbG90IG9mIHZhbHVlIGZvciBtb3RyIikKYGBgCgpgYGB7cn0KcHJvdm9fZGYgJT4lCiAgZmlsdGVyKG1lYXN1cmUgPT0gImV5ZXRyX3ZhbHVlIikgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsIGNvbG9yPW1ldHJpYykpICsKICAgIGdlb21fZGVuc2l0eSgpICsKICAgIHhsYWIoIlJlYWRpbmcgTWVhc3VyZSIpICsKICAgIGdndGl0bGUoIkRlbnNpdHkgcGxvdCBvZiB2YWx1ZSBmb3IgZXlldHIiKQpgYGAKCgpgYGB7cn0KZ2RfZGYgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAidG90YWxfZHVyYXRpb24iKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQojIFZpZXcoZ2RfZGYpCgpjb3IudGVzdChnZF9kZiRleWV0cl92YWx1ZSwgZ2RfZGYkbW90cl92YWx1ZSkgICAgICAgICAgI2NvciByaXNlIGZyb20gMC41MjkgdG8gMC41OTg5IAoKcHJvdm9fZGYgJT4lCiAgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBtb3RyX3ZhbHVlLCB5PWV5ZXRyX3ZhbHVlLCBjb2xvcj1tZXRyaWMpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICBmYWNldF93cmFwKC5+bWV0cmljLCBzY2FsZXM9ImZyZWUiKSArCiAgICBnZW9tX3Ntb290aCgpCgojIGdnc2F2ZSgiLi4vdmlzdWFsaXphdGlvbi9tZXRyaWNfY29yLnBuZyIsIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDIuNSkKCmBgYAoKYGBge3J9CmdkX2RmXzIgPSBwcm92b19kZiAlPiUgZmlsdGVyKG1ldHJpYyA9PSAiZmlyc3RfZHVyYXRpb24iKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQoKY29yLnRlc3QoZ2RfZGZfMiRleWV0cl92YWx1ZSwgZ2RfZGZfMiRtb3RyX3ZhbHVlKQpgYGAKCmBgYHtyfQpnZF9kZl8zID0gcHJvdm9fZGYgJT4lIGZpbHRlcihtZXRyaWMgPT0gImdhemVfZHVyYXRpb24iKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQoKY29yLnRlc3QoZ2RfZGZfMyRleWV0cl92YWx1ZSwgZ2RfZGZfMyRtb3RyX3ZhbHVlKQpgYGAKCgpgYGB7cn0KZ2RfZGZfNCA9IHByb3ZvX2RmICU+JSBmaWx0ZXIobWV0cmljID09ICJnb19wYXN0X3RpbWUiKSAlPiUgc3ByZWFkKG1lYXN1cmUsIHZhbHVlKQoKY29yLnRlc3QoZ2RfZGZfNCRleWV0cl92YWx1ZSwgZ2RfZGZfNCRtb3RyX3ZhbHVlKQoKYGBgCmBgYHtyfQpwcm92b19kZiAlPiUKICBnYXRoZXIod29yZF9wcm9wLCB3b3JkX3Byb3BfdmFsLCBjKCJmcmVxIiwgImxlbiIsICJzdXJwIikpICU+JQogIGZpbHRlcihtZXRyaWMgPT0gImdhemVfZHVyYXRpb24iKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeT13b3JkX3Byb3BfdmFsLCBjb2xvciA9IG1lYXN1cmUpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICBmYWNldF9ncmlkKHdvcmRfcHJvcH5tZWFzdXJlLCBzY2FsZXM9ImZyZWUiKSArCiAgICBnZW9tX3Ntb290aCgpICsKICAgIHhsYWIoIlJlYWRpbmcgTWVhc3VyZSIpCgojIGdnc2F2ZSgiLi4vdmlzdWFsaXphdGlvbi93b3JkX3Byb3BfY29tcHMucG5nIiwgZGV2aWNlID0gInBuZyIsIHdpZHRoID0gNiwgaGVpZ2h0ID0gMykKYGBgCgpgYGB7cn0KcHJvdm9fZGYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsIHk9ZnJlcSwgY29sb3I9bWV0cmljKSkgKwogICAgZ2VvbV9wb2ludChhbHBoYSA9IDAuMikgKwogICAgZmFjZXRfZ3JpZChtZXRyaWN+bWVhc3VyZSwgc2NhbGVzPSJmcmVlIikgKwogICAgZ2VvbV9zbW9vdGgoKQpgYGAKCmBgYHtyfQpwcm92b19kZiAlPiUKICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSwgeT1zdXJwLCBjb2xvcj1tZXRyaWMpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gMC4yKSArCiAgICBmYWNldF9ncmlkKG1ldHJpY35tZWFzdXJlLCBzY2FsZXM9ImZyZWUiKSArCiAgICBnZW9tX3Ntb290aCgpCmBgYAoKCgojIyBTaGFwZSBvZiBzdXJwcmlzYWwgLyBSVCByZWxhdGlvbnNoaXAKCmBgYHtyfQoKZml0X2dhbV9pbm5lciA9IGZ1bmN0aW9uKGJvb3RzdHJhcF9zYW1wbGUsIG1lYW5fcHJlZGljdG9ycykgewogIAogIGRmID0gYm9vdHN0cmFwX3NhbXBsZSRkYXRhCiAgd2VpZ2h0cyA9IHRhYnVsYXRlKGFzLmludGVnZXIoYm9vdHN0cmFwX3NhbXBsZSksIG5yb3coZGYpKQogIAogIG0gPSBnYW0ocHN5Y2hvbWV0cmljIH4gcyhzdXJwLCBicyA9ICdjcicsIGsgPSA2KSArIHMocHJldl9zdXJwLCBicyA9ICdjcicsIGsgPSA2KSArIHRlKGZyZXEsIGxlbiwgYnMgPSAnY3InKSArIHRlKHByZXZfZnJlcSwgcHJldl9sZW4sIGJzID0gJ2NyJyksIGRhdGEgPSBkZiwgd2VpZ2h0cyA9IHdlaWdodHMpCiAgdGVybXNfdG9fcHJlZGljdCA9IGMoInMoc3VycCkiLCAicyhwcmV2X3N1cnApIikKICAKICBuZXdkYXRhID0gZGF0YS5mcmFtZShzdXJwPXNlcSgwLDIwLGJ5PTAuMSksIHByZXZfc3VycD1tZWFuX3ByZWRpY3RvcnMkc3VycCwKICAgICAgICAgICAgICAgICAgICAgICBmcmVxPW1lYW5fcHJlZGljdG9ycyRmcmVxLCBwcmV2X2ZyZXE9bWVhbl9wcmVkaWN0b3JzJGZyZXEsCiAgICAgICAgICAgICAgICAgICAgICAgbGVuPW1lYW5fcHJlZGljdG9ycyRmcmVxLCBwcmV2X2xlbj1tZWFuX3ByZWRpY3RvcnMkZnJlcSkKCiAgIyBSZXR1cm5zIGEgbWF0cml4IE5fc2FtcGxlcyAqIE5fdGVybXMuCiAgcGVyX3Rlcm1fcHJlZGljdGlvbnMgPSBwcmVkaWN0KG0sIG5ld2RhdGE9bmV3ZGF0YSwgdGVybXM9dGVybXNfdG9fcHJlZGljdCwgdHlwZT0idGVybXMiKQoKICAjIEFkZGl0aXZlIG1vZGVsIC0tIHN1bSBhY3Jvc3MgcHJlZGljdG9yIHJlc3BvbnNlIGNvbnRyaWJ1dGlvbnMgKG1hdHJpeCBjb2x1bW5zKS4KICBwcmVkaWN0aW9ucyA9IHJvd1N1bXMocGVyX3Rlcm1fcHJlZGljdGlvbnMpCgogIHJldHVybihuZXdkYXRhICU+JSBtdXRhdGUoeT1wcmVkaWN0aW9ucykpCn0KCmZpdF9nYW0gPSBmdW5jdGlvbihkZiwgbWVhbl9wcmVkaWN0b3JzLCBhbHBoYT0wLjA1KSB7CiAgIyBCb290c3RyYXAtcmVzYW1wbGUgZGF0YQogIGJvb3RfbW9kZWxzID0gZGYgJT4lIGJvb3RzdHJhcHModGltZXM9MTApICU+JSAKICAgIyBGaXQgYSBHQU0gYW5kIGdldCBwcmVkaWN0aW9ucyBmb3IgZWFjaCBzYW1wbGUKICAgIG11dGF0ZShzbW9vdGhlZD1tYXAoc3BsaXRzLCBmaXRfZ2FtX2lubmVyLCBtZWFuX3ByZWRpY3RvcnM9bWVhbl9wcmVkaWN0b3JzKSkKICAKICAjIEV4dHJhY3QgbWVhbiBhbmQgNSUgYW5kIDk1JSBwZXJjZW50aWxlIHktdmFsdWVzIGZvciBlYWNoIHN1cnByaXNhbCB2YWx1ZQogIHJlc3VsdCA9IGJvb3RfbW9kZWxzICU+JSAKICAgIHVubmVzdChzbW9vdGhlZCkgJT4lIAogICAgZHBseXI6OnNlbGVjdChzdXJwLCB5KSAlPiUgCiAgICBncm91cF9ieShzdXJwKSAlPiUgCiAgICAgIHN1bW1hcmlzZSh5X2xvd2VyPXF1YW50aWxlKHksIGFscGhhIC8gMiksIAogICAgICAgICAgICAgICAgeV91cHBlcj1xdWFudGlsZSh5LCAxIC0gYWxwaGEgLyAyKSwKICAgICAgICAgICAgICAgIHk9bWVhbih5KSkgJT4lIAogICAgdW5ncm91cCgpCiAgCiAgcmV0dXJuIChyZXN1bHQpCn0KCgpgYGAKCgpgYGB7cn0KCmdhbV9tb2RlbGluZ19kZiA9IHByb3ZvX2RmICU+JQogIHNwcmVhZChtZWFzdXJlLCB2YWx1ZSkgJT4lCiAgbXV0YXRlKGxlbiA9IG5jaGFyKHdvcmQpKSAlPiUKICBncm91cF9ieShtZXRyaWMsIHRleHRfaWQpICU+JQogICAgYXJyYW5nZSh3b3JkX3RleHRfaWR4KSAlPiUKICAgIG11dGF0ZShwcmV2X3N1cnAgPSBsYWcoc3VycCksCiAgICAgICAgICAgcHJldl9mcmVxID0gbGFnKGZyZXEpLAogICAgICAgICAgIHByZXZfbGVuID0gbGFnKGxlbiksCiAgICAgICAgICAgcHJldl9leWV0cl92YWx1ZSA9IGxhZyhleWV0cl92YWx1ZSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBkcm9wX25hKCkgJT4lCiAgcmVuYW1lKHBzeWNob21ldHJpYyA9IG1vdHJfdmFsdWUpCgoKc21vb3Roc19kZiA9IGRhdGEuZnJhbWUoKQoKbWV0cmljcyA9IGMoImdhemVfZHVyYXRpb24iLCAidG90YWxfZHVyYXRpb24iLCAiZ29fcGFzdF90aW1lIiwgImZpcnN0X2R1cmF0aW9uIikKZm9yIChtIGluIG1ldHJpY3MpIHsKICBwcmludChwYXN0ZTAoIkZpdHRpbmcgbW9kZWwgZm9yICIsIG0pKQogIGR1bW15X2RmID0gZ2FtX21vZGVsaW5nX2RmICU+JSBmaWx0ZXIobWV0cmljID09IG0pCiAgbWVhbl9wcmVkaWN0b3JzID0gZHVtbXlfZGYgJT4lIHN1bW1hcmlzZShzdXJwID0gbWVhbihzdXJwKSwgbGVuID0gbWVhbihsZW4pLCBmcmVxID0gbWVhbihmcmVxKSkKICBzbW9vdGhzID0gZHVtbXlfZGYgJT4lIGZpdF9nYW0oLiwgbWVhbl9wcmVkaWN0b3JzKQogICNGaXggMCBzdXJwcmlzYWwgPSAwIG1zCiAgZ2FtX3Ntb290aHMgPSBzbW9vdGhzICU+JSBtdXRhdGUoZGVsdGEgPSAwIC0geVsxXSwgeT15ICsgZGVsdGEsIHlfbG93ZXI9IHlfbG93ZXIgKyBkZWx0YSwgeV91cHBlcj15X3VwcGVyICsgZGVsdGEpCiAgc21vb3Roc19kZiA9IHJiaW5kKHNtb290aHNfZGYsIGdhbV9zbW9vdGhzICU+JSBtdXRhdGUocHN5Y2hvbWV0cmljID0gbSkpCn0KCmBgYAoKCmBgYHtyfQojIFN1cnByaXNhbCBjdXJ2ZXMKICBnZ3Bsb3QoKSArCiAgICAgIGdlb21fbGluZShkYXRhID0gc21vb3Roc19kZiwgYWVzKHg9c3VycCwgeT15LCBjb2xvciA9IHBzeWNob21ldHJpYyksIHNpemU9MC43KSArCiAgICAgIGdlb21fcmliYm9uKGRhdGEgPSBzbW9vdGhzX2RmLCBhZXMoeD1zdXJwLCB5bWluPXlfbG93ZXIsIHltYXg9eV91cHBlciwgZmlsbCA9IHBzeWNob21ldHJpYyksIGFscGhhPTAuMywgc2l6ZT0wLjUpICsKICAgICAgc2NhbGVfeF9jb250aW51b3VzKGxhYmVscz1jKDAsIDEwLCAyMCksIGJyZWFrcz1jKDAsIDEwLCAyMCksIG1pbm9yX2JyZWFrcyA9IE5VTEwpICsKICAgICAgZmFjZXRfd3JhcChwc3ljaG9tZXRyaWN+LikgKwogICAgICB5bGFiKCJTbG93ZG93biBkdWUgdG8gU3VycHJpc2FsIChtcykiKSArCiAgICAgIHhsYWIoIlN1cnByaXNhbCBvZiBXb3JkIikgKwogICAgICBnZ3RpdGxlKCJSZWxhdGlvbnNoaXAgYmV0d2VlbiBNb1RSIFRpbWVzIGFuZCBTdXJwcmlzYWwiKQogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKQogICkKYGBgCgoKCgoKCmBgYHtyfQoKCgoKCmBgYAoKIyBUYXJnZXRlZCBFdmFsdWF0aW9uIERhdGEKCgoKYGBge3J9CiMgVmlldyhtb3RyX2RmKQoKbW90cl9hdHRhY2hfZGYgPSBtb3RyX2RmICU+JQogIGZpbHRlcihleHByX2lkID09ICJBdHRhY2htZW50IikgJT4lCiAgcmVuYW1lKCBpdGVtX2lkID0gcGFyYV9ucikgJT4lCiAgbXV0YXRlKGl0ZW1faWQgPSBhcy5pbnRlZ2VyKGl0ZW1faWQpKSAlPiUKICBtdXRhdGUoY29uZF9pZCA9IGFzLmZhY3Rvcihjb25kX2lkKSkgJT4lCiAgbXV0YXRlKGNvbmRfaWQgPSBpZl9lbHNlKGNvbmRfaWQgPT0gMSwgIk5vIENvbW1hIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShjb25kX2lkID09IDIsICJDb21tYSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShjb25kX2lkID09IDMsICJhZHZfaGlnaCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2UoY29uZF9pZCA9PSA0LCAiYWR2X2xvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShjb25kX2lkID09IDUsICJyZWxhdGl2ZV9oaWdoIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmX2Vsc2UoY29uZF9pZCA9PSA2LCAicmVsYXRpdmVfbG93IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZfZWxzZShjb25kX2lkID09IDcsICJwcmFjdGljZSIsICJmaWxsZXIiKSkpKSkpKSkgJT4lCiAgZmlsdGVyKG1ldHJpYyAhPSAiRlBSZWciKSAlPiUKICBmaWx0ZXIoY29uZF9pZCA9PSAicmVsYXRpdmVfaGlnaCIgfCBjb25kX2lkID09ICJyZWxhdGl2ZV9sb3ciKSAlPiUKCiAgIyBmaWx0ZXIoISAoaXRlbV9pZCA9PSA0ICYgY29uZF9pZCA9PSAiTm8gQ29tbWEiKSApICU+JSAjIGp1c3QgYmVjYXVzZSBvZiBhbGlnbm1lbnQgaXNzdWVzIGZvciBub3cKICAKICBtdXRhdGUoY3JpdCA9IGlmX2Vsc2Uod29yZCA9PSAid2hvIiwgd29yZF9uciwgYXMuaW50ZWdlcigwKSApKSAlPiUKICBncm91cF9ieShjb25kX2lkLCBpdGVtX2lkKSAlPiUKICAgIG11dGF0ZShjcml0ID0gdW5pcXVlKGNyaXQpWzJdKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHdvcmRfbnIgPSB3b3JkX25yIC0gY3JpdCkKVmlldyhtb3RyX2F0dGFjaF9kZikKCgphZ2dfbW90cl9hdHRhY2hfZGYgPSBtb3RyX2F0dGFjaF9kZiAlPiUKICBkcm9wX25hKCkgJT4lCiAgZmlsdGVyKHdvcmRfbnIgPj0gLTIsIHdvcmRfbnIgPCA2KSAlPiUKICBncm91cF9ieShjb25kX2lkLCB3b3JkX25yLCBtZXRyaWMpICU+JQogICAgc3VtbWFyaXNlKCBtID0gbWVhbih2YWx1ZSksCiAgICAgICAgICAgICAgIHNkID0gc3RkLmVycm9yKHZhbHVlKSwKICAgICAgICAgICAgICAgdXBwZXIgPSBtICsgMS45NiAqIHNkLAogICAgICAgICAgICAgICBsb3dlciA9IG0gLSAxLjk4ICogc2QsCiAgICAgICAgICAgICAgIG4gPSBuKCkpICU+JQogIHVuZ3JvdXAoKQoKVmlldyhhZ2dfbW90cl9hdHRhY2hfZGYpCgphZ2dfbW90cl9hdHRhY2hfZGYgJT4lCiAgZ2dwbG90KGFlcyh4ID0gd29yZF9uciwgeSA9IG0sIGNvbG9yID0gY29uZF9pZCkpICsKICAgIGdlb21fcmVjdChhZXMoeG1pbiA9IDIuNSwgeG1heCA9IDUuNSwgeW1pbiA9IDEwMCwgeW1heCA9IDgwMCksIGZpbGw9YWxwaGEoIndoaXRlIiwgMCksIGNvbG9yID0gIiM0NWVmNzAiLCBsaW5ldHlwZSA9ICJkb3R0ZWQiKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZ2VvbV9lcnJvcmJhcihhZXMoeW1heCA9IHVwcGVyLCB5bWluID0gbG93ZXIpLCB3aWR0aCA9IDAuMykgKwogICAgZ2VvbV9saW5lKCkgKwogICAgI2dlb21fdGV4dChhZXMobGFiZWwgPSB3b3JkLCB5ID0gaWZfZWxzZShjb25kX2lkID09ICJDb21tYSIsIDMwMDAsIDM1MDApKSwgc2l6ZSA9IDIpICsKICAgICNmYWNldF9ncmlkKHBhcmFfbnJ+Y29uZF9pZCkgKwogIHlsYWIoIlJlYWRpbmcgVGltZSIpICsKICB4bGFiKCJDb25kaXRpb24iKSArCiAgIyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPS0yOjUsIGxhYmVscz1jKCJ0aGUiLCAibWFuIiwgImFuZCIsICJoaXMiLCAid2lmZSIsICJyYW4iLCAiYXdheSIsICJmcm9tIikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPS0yOjUsIGxhYmVscz1jKCd0aGUnLCAncXVlZW4nLCAnd2hvJywgJ3ByYWlzZWQnLCAnaGVyc2VsZicsICdhbGwnLCAndGhlJywgJ3RpbWUnKSkgKwogIGZhY2V0X2dyaWQofm1ldHJpYykgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEpCiAgKQoKI2dnc2F2ZSgiLi4vdmlzdWFsaXphdGlvbi9hdHRhY2htZW50LnBuZyIsIGRldmljZSA9ICJwbmciLCB3aWR0aCA9IDYsIGhlaWdodCA9IDMpCgpgYGAKCmBgYHtyfQoKb3B0aW9ucyhKVUxJQV9IT01FID0gIi9BcHBsaWNhdGlvbnMvSnVsaWEtMS44LmFwcC9Db250ZW50cy9SZXNvdXJjZXMvanVsaWEvYmluLyIpCiMgbGlicmFyeShqZ2xtbSkKIyBqZ2xtbV9zZXR1cCgpCgphdHRhY2hfbG1fZGYgPSBtb3RyX2F0dGFjaF9kZiAlPiUKICBmaWx0ZXIobWV0cmljID09ICJnYXplX2R1cmF0aW9uIikgJT4lCiAgZmlsdGVyKHdvcmRfbnIgPT0gMykgJT4lCiAgbXV0YXRlKGl0ZW1faWQgPSBhcy5mYWN0b3IoaXRlbV9pZCksCiAgICAgICAgIHN1YmogPSBhcy5mYWN0b3Ioc3ViaikpCgptID0gYXR0YWNoX2xtX2RmICU+JQogIGxtZXIodmFsdWUgfiBjb25kX2lkICsgKGNvbmRfaWQgfCBpdGVtX2lkKSArIChjb25kX2lkIHwgc3ViaiksIGRhdGE9LikKCnN1bW1hcnkobSkKCmBgYAoKCgoK